package com.example.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class ScheduledExecutorServiceExampleService {

    // 创建一个包含两个线程的定时调度线程池
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);

    /**
     * 演示三种不同类型的定时任务：
     * 1. 延迟任务：等待5秒后执行
     * 2. 固定速率任务：每隔3秒执行一次，首次延迟1秒执行
     * 3. 固定延迟任务：首次延迟1秒执行，之后每次执行完后再等待3秒继续执行
     *
     *
     * scheduleAtFixedRate 和 scheduleWithFixedDelay 的区别主要在于任务调度的时机：
     *
     * scheduleAtFixedRate:
     * 任务按照固定的时间间隔启动，不考虑任务的执行时间。也就是说，下一次任务的启动时间是基于上一次任务的启动时间计算的，而不是结束时间。
     * 如果某个任务执行时间超过了预定的间隔时间，下一次任务可能会立即执行，可能导致任务重叠。
     * 适用于那些需要严格按照固定频率运行的任务，比如每隔5分钟触发的心跳检测。
     *
     * scheduleWithFixedDelay:
     * 任务会在前一个任务执行完成后的固定延迟时间后启动。也就是说，下一次任务的启动时间是基于上一次任务完成的时间计算的。
     * 这种方式保证了每次任务之间都有固定的休息时间，适用于那些执行时间不确定，并且需要在任务之间留有固定间隔的情况，比如日志清理或数据同步。
     *
     * 示例场景：
     * scheduleAtFixedRate：假设你有一个需要每隔5分钟准时执行的任务，比如监测系统状态，即使任务本身需要更长时间完成，下次任务依然会在固定时间点启动。
     * scheduleWithFixedDelay：适合于任务执行时间不确定的场景，比如文件备份，备份完成后等待5分钟再启动下一个备份。这样可以保证任务之间的间隔时间是固定的。
     */
    public void scheduleTasks() {
        log.info("计划延迟任务，5秒后执行...");
        scheduler.schedule(() -> log.info("延迟任务执行"), 5, TimeUnit.SECONDS);

        log.info("计划固定速率任务，每隔3秒执行一次，首次延迟1秒...");
        scheduler.scheduleAtFixedRate(() -> log.info("定期任务执行: 当前线程 {}", Thread.currentThread().getName()),
                1, 3, TimeUnit.SECONDS);

        log.info("计划固定延迟任务，首次延迟1秒，之后每次执行完后等待3秒...");
        scheduler.scheduleWithFixedDelay(() -> {
            log.info("固定延迟任务开始执行: 当前线程 {}", Thread.currentThread().getName());
            // 模拟一些任务耗时
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.error("固定延迟任务被中断", e);
            }
            log.info("固定延迟任务完成");
        }, 1, 3, TimeUnit.SECONDS);
    }

    /**
     * 关闭调度器的方法，确保程序结束时可以干净地关闭线程池。
     */
    public void shutdownScheduler() {
        log.info("正在关闭调度器...");
        scheduler.shutdown();
        try {
            if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
                log.warn("调度器未在5秒内终止，强制关闭...");
                scheduler.shutdownNow();
            }
        } catch (InterruptedException e) {
            log.error("调度器关闭时被中断", e);
            scheduler.shutdownNow();
        }
    }
}
